/*
 * ltree 和 lquery 的输入/输出函数
 * Teodor Sigaev <teodor@stack.net>
 * contrib/ltree/ltree_io.c
 */
#include "postgres.h"

#include <ctype.h>

#include "crc32.h"
#include "libpq/pqformat.h"
#include "ltree.h"
#include "utils/memutils.h"


typedef struct
{
	const char *start;
	int			len;			/* 以字节为单位的长度 */
	int			flag;
	int			wlen;			/* 以字符为单位的长度 */
} nodeitem;

#define LTPRS_WAITNAME	0
#define LTPRS_WAITDELIM 1

static void fc_finish_nodeitem(nodeitem *fc_lptr, const char *fc_ptr,
							bool fc_is_lquery, int fc_pos);


/*
 * 期望一个以空字符结尾的字符串
 * 返回一个 ltree
 */
static ltree * fc_parse_ltree(const char *fc_buf)
{
	const char *fc_ptr;
	nodeitem   *fc_list,
			   *fc_lptr;
	int			fc_num = 0,
				fc_totallen = 0;
	int			fc_state = LTPRS_WAITNAME;
	ltree	   *fc_result;
	ltree_level *fc_curlevel;
	int			fc_charlen;
	int			fc_pos = 1;		/* 错误消息的字符位置 */

#define UNCHAR ereport(ERROR, \
					   errcode(ERRCODE_SYNTAX_ERROR), \
					   errmsg("ltree syntax error at character %d", \
							  fc_pos))

	fc_ptr = fc_buf;
	while (*fc_ptr)
	{
		fc_charlen = pg_mblen(fc_ptr);
		if (t_iseq(fc_ptr, '.'))
			fc_num++;
		fc_ptr += fc_charlen;
	}

	if (fc_num + 1 > LTREE_MAX_LEVELS)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)",
						fc_num + 1, LTREE_MAX_LEVELS)));
	fc_list = fc_lptr = (nodeitem *) palloc(sizeof(nodeitem) * (fc_num + 1));
	fc_ptr = fc_buf;
	while (*fc_ptr)
	{
		fc_charlen = pg_mblen(fc_ptr);

		switch (fc_state)
		{
			case LTPRS_WAITNAME:
				if (ISALNUM(fc_ptr))
				{
					fc_lptr->start = fc_ptr;
					fc_lptr->wlen = 0;
					fc_state = LTPRS_WAITDELIM;
				}
				else
					UNCHAR;
				break;
			case LTPRS_WAITDELIM:
				if (t_iseq(fc_ptr, '.'))
				{
					fc_finish_nodeitem(fc_lptr, fc_ptr, false, fc_pos);
					fc_totallen += MAXALIGN(fc_lptr->len + LEVEL_HDRSIZE);
					fc_lptr++;
					fc_state = LTPRS_WAITNAME;
				}
				else if (!ISALNUM(fc_ptr))
					UNCHAR;
				break;
			default:
				elog(ERROR, "internal error in ltree parser");
		}

		fc_ptr += fc_charlen;
		fc_lptr->wlen++;
		fc_pos++;
	}

	if (fc_state == LTPRS_WAITDELIM)
	{
		fc_finish_nodeitem(fc_lptr, fc_ptr, false, fc_pos);
		fc_totallen += MAXALIGN(fc_lptr->len + LEVEL_HDRSIZE);
		fc_lptr++;
	}
	else if (!(fc_state == LTPRS_WAITNAME && fc_lptr == fc_list))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("ltree syntax error"),
				 errdetail("Unexpected end of input.")));

	fc_result = (ltree *) palloc0(LTREE_HDRSIZE + fc_totallen);
	SET_VARSIZE(fc_result, LTREE_HDRSIZE + fc_totallen);
	fc_result->numlevel = fc_lptr - fc_list;
	fc_curlevel = LTREE_FIRST(fc_result);
	fc_lptr = fc_list;
	while (fc_lptr - fc_list < fc_result->numlevel)
	{
		fc_curlevel->len = (uint16) fc_lptr->len;
		memcpy(fc_curlevel->name, fc_lptr->start, fc_lptr->len);
		fc_curlevel = LEVEL_NEXT(fc_curlevel);
		fc_lptr++;
	}

	pfree(fc_list);
	return fc_result;

#undef UNCHAR
}

/*
 * 期望一个 ltree
 * 返回一个以空字符结尾的字符串
 */
static char * fc_deparse_ltree(const ltree *fc_in)
{
	char	   *fc_buf,
			   *fc_ptr;
	int			fc_i;
	ltree_level *fc_curlevel;

	fc_ptr = fc_buf = (char *) palloc(VARSIZE(fc_in));
	fc_curlevel = LTREE_FIRST(fc_in);
	for (fc_i = 0; fc_i < fc_in->numlevel; fc_i++)
	{
		if (fc_i != 0)
		{
			*fc_ptr = '.';
			fc_ptr++;
		}
		memcpy(fc_ptr, fc_curlevel->name, fc_curlevel->len);
		fc_ptr += fc_curlevel->len;
		fc_curlevel = LEVEL_NEXT(fc_curlevel);
	}

	*fc_ptr = '\0';
	return fc_buf;
}

/*
 * 基本的 ltree 输入/输出函数
 */
PG_FUNCTION_INFO_V1(ltree_in);
Datum ltree_in(PG_FUNCTION_ARGS)
{
	char	   *fc_buf = (char *) PG_GETARG_POINTER(0);

	PG_RETURN_POINTER(fc_parse_ltree(fc_buf));
}

PG_FUNCTION_INFO_V1(ltree_out);
Datum ltree_out(PG_FUNCTION_ARGS)
{
	ltree	   *fc_in = PG_GETARG_LTREE_P(0);

	PG_RETURN_POINTER(fc_deparse_ltree(fc_in));
}

/*
 * ltree 类型发送函数
 *
 * 类型以文本形式发送为二进制模式，因此这几乎与输出函数相同，
 * 但它带有版本号的前缀，以便我们可以在未来如果需要的话更改发送的二进制格式。目前，
 * 只支持版本 1。
 */
PG_FUNCTION_INFO_V1(ltree_send);
Datum ltree_send(PG_FUNCTION_ARGS)
{
	ltree	   *fc_in = PG_GETARG_LTREE_P(0);
	StringInfoData fc_buf;
	int			fc_version = 1;
	char	   *fc_res = fc_deparse_ltree(fc_in);

	pq_begintypsend(&fc_buf);
	pq_sendint8(&fc_buf, fc_version);
	pq_sendtext(&fc_buf, fc_res, strlen(fc_res));
	pfree(fc_res);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 * ltree 类型接收函数
 *
 * 类型以文本形式发送为二进制模式，因此这几乎与输入函数相同，
 * 但它带有版本号的前缀，以便我们可以在未来如果需要的话更改发送的二进制格式。目前，
 * 只支持版本 1。
 */
PG_FUNCTION_INFO_V1(ltree_recv);
Datum ltree_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	int			fc_version = pq_getmsgint(fc_buf, 1);
	char	   *fc_str;
	int			fc_nbytes;
	ltree	   *fc_res;

	if (fc_version != 1)
		elog(ERROR, "unsupported ltree version number %d", fc_version);

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	fc_res = fc_parse_ltree(fc_str);
	pfree(fc_str);

	PG_RETURN_POINTER(fc_res);
}


#define LQPRS_WAITLEVEL 0
#define LQPRS_WAITDELIM 1
#define LQPRS_WAITOPEN	2
#define LQPRS_WAITFNUM	3
#define LQPRS_WAITSNUM	4
#define LQPRS_WAITND	5
#define LQPRS_WAITCLOSE 6
#define LQPRS_WAITEND	7
#define LQPRS_WAITVAR	8


#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
#define ITEMSIZE	MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
#define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )

/*
 * 期望一个 lquery
 * 返回一个以空字符结尾的字符串
 */
static lquery *
fc_parse_lquery(const char *fc_buf)
{
	const char *fc_ptr;
	int			fc_num = 0,
				fc_totallen = 0,
				fc_numOR = 0;
	int			fc_state = LQPRS_WAITLEVEL;
	lquery	   *fc_result;
	nodeitem   *fc_lptr = NULL;
	lquery_level *fc_cur,
			   *fc_curqlevel,
			   *fc_tmpql;
	lquery_variant *fc_lrptr = NULL;
	bool		fc_hasnot = false;
	bool		fc_wasbad = false;
	int			fc_charlen;
	int			fc_pos = 1;		/* 错误消息的字符位置 */

#define UNCHAR ereport(ERROR, \
					   errcode(ERRCODE_SYNTAX_ERROR), \
					   errmsg("lquery syntax error at character %d", \
							  fc_pos))

	fc_ptr = fc_buf;
	while (*fc_ptr)
	{
		fc_charlen = pg_mblen(fc_ptr);

		if (t_iseq(fc_ptr, '.'))
			fc_num++;
		else if (t_iseq(fc_ptr, '|'))
			fc_numOR++;

		fc_ptr += fc_charlen;
	}

	fc_num++;
	if (fc_num > LQUERY_MAX_LEVELS)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)",
						fc_num, LQUERY_MAX_LEVELS)));
	fc_curqlevel = fc_tmpql = (lquery_level *) palloc0(ITEMSIZE * fc_num);
	fc_ptr = fc_buf;
	while (*fc_ptr)
	{
		fc_charlen = pg_mblen(fc_ptr);

		switch (fc_state)
		{
			case LQPRS_WAITLEVEL:
				if (ISALNUM(fc_ptr))
				{
					GETVAR(fc_curqlevel) = fc_lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (fc_numOR + 1));
					fc_lptr->start = fc_ptr;
					fc_state = LQPRS_WAITDELIM;
					fc_curqlevel->numvar = 1;
				}
				else if (t_iseq(fc_ptr, '!'))
				{
					GETVAR(fc_curqlevel) = fc_lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (fc_numOR + 1));
					fc_lptr->start = fc_ptr + 1;
					fc_lptr->wlen = -1;	/* 抵消下面的计数 ! */
					fc_state = LQPRS_WAITDELIM;
					fc_curqlevel->numvar = 1;
					fc_curqlevel->flag |= LQL_NOT;
					fc_hasnot = true;
				}
				else if (t_iseq(fc_ptr, '*'))
					fc_state = LQPRS_WAITOPEN;
				else
					UNCHAR;
				break;
			case LQPRS_WAITVAR:
				if (ISALNUM(fc_ptr))
				{
					fc_lptr++;
					fc_lptr->start = fc_ptr;
					fc_state = LQPRS_WAITDELIM;
					fc_curqlevel->numvar++;
				}
				else
					UNCHAR;
				break;
			case LQPRS_WAITDELIM:
				if (t_iseq(fc_ptr, '@'))
				{
					fc_lptr->flag |= LVAR_INCASE;
					fc_curqlevel->flag |= LVAR_INCASE;
				}
				else if (t_iseq(fc_ptr, '*'))
				{
					fc_lptr->flag |= LVAR_ANYEND;
					fc_curqlevel->flag |= LVAR_ANYEND;
				}
				else if (t_iseq(fc_ptr, '%'))
				{
					fc_lptr->flag |= LVAR_SUBLEXEME;
					fc_curqlevel->flag |= LVAR_SUBLEXEME;
				}
				else if (t_iseq(fc_ptr, '|'))
				{
					fc_finish_nodeitem(fc_lptr, fc_ptr, true, fc_pos);
					fc_state = LQPRS_WAITVAR;
				}
				else if (t_iseq(fc_ptr, '{'))
				{
					fc_finish_nodeitem(fc_lptr, fc_ptr, true, fc_pos);
					fc_curqlevel->flag |= LQL_COUNT;
					fc_state = LQPRS_WAITFNUM;
				}
				else if (t_iseq(fc_ptr, '.'))
				{
					fc_finish_nodeitem(fc_lptr, fc_ptr, true, fc_pos);
					fc_state = LQPRS_WAITLEVEL;
					fc_curqlevel = NEXTLEV(fc_curqlevel);
				}
				else if (ISALNUM(fc_ptr))
				{
					/* 标志后不允许更多字符 */
					if (fc_lptr->flag)
						UNCHAR;
				}
				else
					UNCHAR;
				break;
			case LQPRS_WAITOPEN:
				if (t_iseq(fc_ptr, '{'))
					fc_state = LQPRS_WAITFNUM;
				else if (t_iseq(fc_ptr, '.'))
				{
					/* 我们只在这里遇到 '*'，因此这些是正确的默认值 */
					fc_curqlevel->low = 0;
					fc_curqlevel->high = LTREE_MAX_LEVELS;
					fc_curqlevel = NEXTLEV(fc_curqlevel);
					fc_state = LQPRS_WAITLEVEL;
				}
				else
					UNCHAR;
				break;
			case LQPRS_WAITFNUM:
				if (t_iseq(fc_ptr, ','))
					fc_state = LQPRS_WAITSNUM;
				else if (t_isdigit(fc_ptr))
				{
					int			fc_low = atoi(fc_ptr);

					if (fc_low < 0 || fc_low > LTREE_MAX_LEVELS)
						ereport(ERROR,
								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
								 errmsg("lquery syntax error"),
								 errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
										   fc_low, LTREE_MAX_LEVELS, fc_pos)));

					fc_curqlevel->low = (uint16) fc_low;
					fc_state = LQPRS_WAITND;
				}
				else
					UNCHAR;
				break;
			case LQPRS_WAITSNUM:
				if (t_isdigit(fc_ptr))
				{
					int			fc_high = atoi(fc_ptr);

					if (fc_high < 0 || fc_high > LTREE_MAX_LEVELS)
						ereport(ERROR,
								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
								 errmsg("lquery syntax error"),
								 errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
										   fc_high, LTREE_MAX_LEVELS, fc_pos)));
					else if (fc_curqlevel->low > fc_high)
						ereport(ERROR,
								(errcode(ERRCODE_SYNTAX_ERROR),
								 errmsg("lquery syntax error"),
								 errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
										   fc_curqlevel->low, fc_high, fc_pos)));

					fc_curqlevel->high = (uint16) fc_high;
					fc_state = LQPRS_WAITCLOSE;
				}
				else if (t_iseq(fc_ptr, '}'))
				{
					fc_curqlevel->high = LTREE_MAX_LEVELS;
					fc_state = LQPRS_WAITEND;
				}
				else
					UNCHAR;
				break;
			case LQPRS_WAITCLOSE:
				if (t_iseq(fc_ptr, '}'))
					fc_state = LQPRS_WAITEND;
				else if (!t_isdigit(fc_ptr))
					UNCHAR;
				break;
			case LQPRS_WAITND:
				if (t_iseq(fc_ptr, '}'))
				{
					fc_curqlevel->high = fc_curqlevel->low;
					fc_state = LQPRS_WAITEND;
				}
				else if (t_iseq(fc_ptr, ','))
					fc_state = LQPRS_WAITSNUM;
				else if (!t_isdigit(fc_ptr))
					UNCHAR;
				break;
			case LQPRS_WAITEND:
				if (t_iseq(fc_ptr, '.'))
				{
					fc_state = LQPRS_WAITLEVEL;
					fc_curqlevel = NEXTLEV(fc_curqlevel);
				}
				else
					UNCHAR;
				break;
			default:
				elog(ERROR, "internal error in lquery parser");
		}

		fc_ptr += fc_charlen;
		if (fc_state == LQPRS_WAITDELIM)
			fc_lptr->wlen++;
		fc_pos++;
	}

	if (fc_state == LQPRS_WAITDELIM)
		fc_finish_nodeitem(fc_lptr, fc_ptr, true, fc_pos);
	else if (fc_state == LQPRS_WAITOPEN)
		fc_curqlevel->high = LTREE_MAX_LEVELS;
	else if (fc_state != LQPRS_WAITEND)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("lquery syntax error"),
				 errdetail("Unexpected end of input.")));

	fc_curqlevel = fc_tmpql;
	fc_totallen = LQUERY_HDRSIZE;
	while ((char *) fc_curqlevel - (char *) fc_tmpql < fc_num * ITEMSIZE)
	{
		fc_totallen += LQL_HDRSIZE;
		if (fc_curqlevel->numvar)
		{
			fc_lptr = GETVAR(fc_curqlevel);
			while (fc_lptr - GETVAR(fc_curqlevel) < fc_curqlevel->numvar)
			{
				fc_totallen += MAXALIGN(LVAR_HDRSIZE + fc_lptr->len);
				fc_lptr++;
			}
		}
		fc_curqlevel = NEXTLEV(fc_curqlevel);
	}

	fc_result = (lquery *) palloc0(fc_totallen);
	SET_VARSIZE(fc_result, fc_totallen);
	fc_result->numlevel = fc_num;
	fc_result->firstgood = 0;
	fc_result->flag = 0;
	if (fc_hasnot)
		fc_result->flag |= LQUERY_HASNOT;
	fc_cur = LQUERY_FIRST(fc_result);
	fc_curqlevel = fc_tmpql;
	while ((char *) fc_curqlevel - (char *) fc_tmpql < fc_num * ITEMSIZE)
	{
		memcpy(fc_cur, fc_curqlevel, LQL_HDRSIZE);
		fc_cur->totallen = LQL_HDRSIZE;
		if (fc_curqlevel->numvar)
		{
			fc_lrptr = LQL_FIRST(fc_cur);
			fc_lptr = GETVAR(fc_curqlevel);
			while (fc_lptr - GETVAR(fc_curqlevel) < fc_curqlevel->numvar)
			{
				fc_cur->totallen += MAXALIGN(LVAR_HDRSIZE + fc_lptr->len);
				fc_lrptr->len = fc_lptr->len;
				fc_lrptr->flag = fc_lptr->flag;
				fc_lrptr->val = ltree_crc32_sz(fc_lptr->start, fc_lptr->len);
				memcpy(fc_lrptr->name, fc_lptr->start, fc_lptr->len);
				fc_lptr++;
				fc_lrptr = LVAR_NEXT(fc_lrptr);
			}
			pfree(GETVAR(fc_curqlevel));
			if (fc_cur->numvar > 1 || fc_cur->flag != 0)
			{
				/* 不是简单匹配 */
				fc_wasbad = true;
			}
			else if (fc_wasbad == false)
			{
				/* 计数前导简单匹配 */
				(fc_result->firstgood)++;
			}
		}
		else
		{
			/* '*', 因此这不是简单匹配 */
			fc_wasbad = true;
		}
		fc_curqlevel = NEXTLEV(fc_curqlevel);
		fc_cur = LQL_NEXT(fc_cur);
	}

	pfree(fc_tmpql);
	return fc_result;

#undef UNCHAR
}

/*
 * 结束解析一个 ltree 或 lquery 节点项：
 * 计算正确的长度，并在不正确时发出警告
 */
static void fc_finish_nodeitem(nodeitem *fc_lptr, const char *fc_ptr, bool fc_is_lquery, int fc_pos)
{
	if (fc_is_lquery)
	{
		/*
		 * 向后退过任何标志字符，并从长度和
		 * 位置中减去它们。
		 */
		while (fc_ptr > fc_lptr->start && strchr("@*%", fc_ptr[-1]) != NULL)
		{
			fc_ptr--;
			fc_lptr->wlen--;
			fc_pos--;
		}
	}

	/* 现在计算字节长度，我们之前没有跟踪。 */
	fc_lptr->len = fc_ptr - fc_lptr->start;

	/* 如果为空或太长则发出警告 */
	if (fc_lptr->len == 0)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 fc_is_lquery ?
				 errmsg("lquery syntax error at character %d", fc_pos) :
				 errmsg("ltree syntax error at character %d", fc_pos),
				 errdetail("Empty labels are not allowed.")));
	if (fc_lptr->wlen > LTREE_LABEL_MAX_CHARS)
		ereport(ERROR,
				(errcode(ERRCODE_NAME_TOO_LONG),
				 errmsg("label string is too long"),
				 errdetail("Label length is %d, must be at most %d, at character %d.",
						   fc_lptr->wlen, LTREE_LABEL_MAX_CHARS, fc_pos)));
}

/*
 * 期望一个 lquery
 * 返回一个以空字符结尾的字符串
 */
static char * fc_deparse_lquery(const lquery *fc_in)
{
	char	   *fc_buf,
			   *fc_ptr;
	int			fc_i,
				fc_j,
				fc_totallen = 1;
	lquery_level *fc_curqlevel;
	lquery_variant *fc_curtlevel;

	fc_curqlevel = LQUERY_FIRST(fc_in);
	for (fc_i = 0; fc_i < fc_in->numlevel; fc_i++)
	{
		fc_totallen++;
		if (fc_curqlevel->numvar)
		{
			fc_totallen += 1 + (fc_curqlevel->numvar * 4) + fc_curqlevel->totallen;
			if (fc_curqlevel->flag & LQL_COUNT)
				fc_totallen += 2 * 11 + 3;
		}
		else
			fc_totallen += 2 * 11 + 4;
		fc_curqlevel = LQL_NEXT(fc_curqlevel);
	}

	fc_ptr = fc_buf = (char *) palloc(fc_totallen);
	fc_curqlevel = LQUERY_FIRST(fc_in);
	for (fc_i = 0; fc_i < fc_in->numlevel; fc_i++)
	{
		if (fc_i != 0)
		{
			*fc_ptr = '.';
			fc_ptr++;
		}
		if (fc_curqlevel->numvar)
		{
			if (fc_curqlevel->flag & LQL_NOT)
			{
				*fc_ptr = '!';
				fc_ptr++;
			}
			fc_curtlevel = LQL_FIRST(fc_curqlevel);
			for (fc_j = 0; fc_j < fc_curqlevel->numvar; fc_j++)
			{
				if (fc_j != 0)
				{
					*fc_ptr = '|';
					fc_ptr++;
				}
				memcpy(fc_ptr, fc_curtlevel->name, fc_curtlevel->len);
				fc_ptr += fc_curtlevel->len;
				if ((fc_curtlevel->flag & LVAR_SUBLEXEME))
				{
					*fc_ptr = '%';
					fc_ptr++;
				}
				if ((fc_curtlevel->flag & LVAR_INCASE))
				{
					*fc_ptr = '@';
					fc_ptr++;
				}
				if ((fc_curtlevel->flag & LVAR_ANYEND))
				{
					*fc_ptr = '*';
					fc_ptr++;
				}
				fc_curtlevel = LVAR_NEXT(fc_curtlevel);
			}
		}
		else
		{
			*fc_ptr = '*';
			fc_ptr++;
		}

		if ((fc_curqlevel->flag & LQL_COUNT) || fc_curqlevel->numvar == 0)
		{
			if (fc_curqlevel->low == fc_curqlevel->high)
			{
				sprintf(fc_ptr, "{%d}", fc_curqlevel->low);
			}
			else if (fc_curqlevel->low == 0)
			{
				if (fc_curqlevel->high == LTREE_MAX_LEVELS)
				{
					if (fc_curqlevel->numvar == 0)
					{
						/* 这是 '*' 的默认值，因此不打印任何内容 */
						*fc_ptr = '\0';
					}
					else
						sprintf(fc_ptr, "{,}");
				}
				else
					sprintf(fc_ptr, "{,%d}", fc_curqlevel->high);
			}
			else if (fc_curqlevel->high == LTREE_MAX_LEVELS)
			{
				sprintf(fc_ptr, "{%d,}", fc_curqlevel->low);
			}
			else
				sprintf(fc_ptr, "{%d,%d}", fc_curqlevel->low, fc_curqlevel->high);
			fc_ptr = strchr(fc_ptr, '\0');
		}

		fc_curqlevel = LQL_NEXT(fc_curqlevel);
	}

	*fc_ptr = '\0';
	return fc_buf;
}

/*
 * 基本的 lquery 输入/输出函数
 */
PG_FUNCTION_INFO_V1(lquery_in);
Datum lquery_in(PG_FUNCTION_ARGS)
{
	char	   *fc_buf = (char *) PG_GETARG_POINTER(0);

	PG_RETURN_POINTER(fc_parse_lquery(fc_buf));
}

PG_FUNCTION_INFO_V1(lquery_out);
Datum lquery_out(PG_FUNCTION_ARGS)
{
	lquery	   *fc_in = PG_GETARG_LQUERY_P(0);

	PG_RETURN_POINTER(fc_deparse_lquery(fc_in));
}

/*
 * lquery 类型发送函数
 *
 * 类型以文本形式发送为二进制模式，因此这几乎与输出函数相同，
 * 但它带有版本号的前缀，以便我们可以在未来如果需要的话更改发送的二进制格式。目前，
 * 只支持版本 1。
 */
PG_FUNCTION_INFO_V1(lquery_send);
Datum lquery_send(PG_FUNCTION_ARGS)
{
	lquery	   *fc_in = PG_GETARG_LQUERY_P(0);
	StringInfoData fc_buf;
	int			fc_version = 1;
	char	   *fc_res = fc_deparse_lquery(fc_in);

	pq_begintypsend(&fc_buf);
	pq_sendint8(&fc_buf, fc_version);
	pq_sendtext(&fc_buf, fc_res, strlen(fc_res));
	pfree(fc_res);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 * lquery 类型接收函数
 *
 * 类型以文本形式发送为二进制模式，因此这几乎与输入函数相同，
 * 但它带有版本号的前缀，以便我们可以在未来如果需要的话更改发送的二进制格式。目前，
 * 只支持版本 1。
 */
PG_FUNCTION_INFO_V1(lquery_recv);
Datum lquery_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	int			fc_version = pq_getmsgint(fc_buf, 1);
	char	   *fc_str;
	int			fc_nbytes;
	lquery	   *fc_res;

	if (fc_version != 1)
		elog(ERROR, "unsupported lquery version number %d", fc_version);

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	fc_res = fc_parse_lquery(fc_str);
	pfree(fc_str);

	PG_RETURN_POINTER(fc_res);
}
